package com.yanfan.data.controller.runtime;

import com.alibaba.fastjson2.JSONObject;
import com.yanfan.common.core.controller.BaseController;
import com.yanfan.common.core.domain.AjaxResult;
import com.yanfan.common.core.mq.InvokeReqDto;
import com.yanfan.common.core.mq.ota.SceneActivelyBo;
import com.yanfan.common.core.page.TableDataInfo;
import com.yanfan.common.core.redis.RedisCache;
import com.yanfan.common.enums.ThingsModelType;
import com.yanfan.common.exception.ServiceException;
import com.yanfan.common.utils.MessageUtils;
import com.yanfan.common.utils.SecurityUtils;
import com.yanfan.common.utils.StringUtils;
import com.yanfan.data.service.IDeviceMessageService;
import com.yanfan.data.service.IPropGetService;
import com.yanfan.framework.manager.AsyncManager;
import com.yanfan.iot.domain.*;
import com.yanfan.iot.model.ThingsModels.ThingsModelValueItem;
import com.yanfan.iot.model.VariableReadVO;
import com.yanfan.iot.service.IDeviceRuntimeService;
import com.yanfan.iot.service.ISceneService;
import com.yanfan.mq.ruleEngine.SceneContext;
import com.yanfan.mq.service.IFunctionInvoke;
import com.yanfan.iot.ruleEngine.FlowLogExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.*;
import java.util.concurrent.ExecutionException;

/**
 * 设备运行时数据controller
 *
 * @author gsb
 * @date 2022/12/5 11:52
 */
@RestController
@RequestMapping("/iot/runtime")
@Api(tags = "设备运行数据")
public class DeviceRuntimeController extends BaseController {

    @Autowired
    private IFunctionInvoke functionInvoke;
    @Autowired
    private IPropGetService propGetService;
    @Resource
    private IDeviceRuntimeService runtimeService;
    @Resource
    private ISceneService sceneService;
    @Resource
    private IDeviceMessageService deviceMessageService;
    @Autowired
    private FlowLogExecutor flowExecutor;
    @Autowired
    private RedisCache redisCache;

    private static Map<Long, Boolean> Map_STR_BOOL = new HashMap<>();

    /**
     * 服务下发返回回执
     */
    @PostMapping(value = "/service/invokeReply")
    ////@PreAuthorize("@ss.hasPermi('iot:service:invokereply')")
    @ApiOperation(value = "服务下发返回回执", httpMethod = "POST", response = AjaxResult.class, notes = "服务下发并返回回执")
    public AjaxResult invokeReply(@Valid @RequestBody InvokeReqDto reqDto) {
        reqDto.setParams(new JSONObject(reqDto.getRemoteCommand()));
        return functionInvoke.invokeReply(reqDto);
    }

    /**
     * 服务下发
     * 例如modbus 格式如下
     *
     * @see InvokeReqDto#getRemoteCommand()
     * key = 寄存器地址
     * value = 寄存器地址值
     * <p>
     * 其他协议 key = identifier
     * value = 值
     * {
     * "serialNumber": "860061060282358",
     * "productId": "2",
     * "identifier": "temp",
     * "remoteCommand": {
     * "4": "4"
     * }
     * }
     */
    @PostMapping("/service/invoke")
    //@PreAuthorize("@ss.hasPermi('iot:service:invoke')")
    @ApiOperation(value = "服务下发", httpMethod = "POST", response = AjaxResult.class, notes = "服务下发")
    public AjaxResult invoke(@Valid @RequestBody InvokeReqDto reqDto) {
        Long userId = SecurityUtils.getLoginUser().getUserId();
        reqDto.setParams(new JSONObject(reqDto.getRemoteCommand()));
        reqDto.setUserId(userId);
        return functionInvoke.invokeNoReply(reqDto);
    }

    /**
     * 执行场景联动
     *
     * @param sceneId 场景联动id
     * @return
     */
    @PostMapping("/runScene")
    //@PreAuthorize("@ss.hasPermi('iot:scene:run')")
    @ApiOperation(value = "服务下发", httpMethod = "POST", response = AjaxResult.class, notes = "场景联动服务下发")
    public AjaxResult runScene(@RequestParam Long sceneId) throws ExecutionException, InterruptedException {
        if (sceneId == null) {
            return AjaxResult.success();
        }
        System.out.println("------------------[执行一次场景联动]---------------------");
        Scene scene = sceneService.selectSceneBySceneId(sceneId);
        // 执行场景规则,异步非阻塞
        SceneContext context = new SceneContext("", 0L, 0, null);
        LiteflowResponse future = flowExecutor.execute2Future(String.valueOf(scene.getChainName()), null, context);
        if (!future.isSuccess()) {
            Exception e = future.getCause();
            System.out.println("场景联动执行失败 message= " + e.getMessage());
        }
        return AjaxResult.success();
    }

    @ApiOperation("主动触发")
    @PostMapping("/service/invokesSceneActively")
    public AjaxResult invokesSceneActively(@RequestBody SceneActivelyBo sceneActivelyBo) {
        try {
            Boolean b = Map_STR_BOOL.get(sceneActivelyBo.getId());
            if (!b){
                Map_STR_BOOL.put(sceneActivelyBo.getId(), true);
            }
            else {
                return AjaxResult.success("上一个任务还在进行，请等一会在执行");
            }
        }catch (Exception e){
            Map_STR_BOOL.put(sceneActivelyBo.getId(),true);
        }
        //先获取主动触发的消息
        List<Script> Scripts = sceneService.getSceneDevice(sceneActivelyBo.getStyle());
        if(Scripts.isEmpty()){
            Map_STR_BOOL.put(sceneActivelyBo.getId(),false);
            return AjaxResult.error("没有触发消息");
        }
        Long userId = SecurityUtils.getLoginUser().getUserId();
        AsyncManager.me().execute(new TimerTask() {
            @Override
            public void run() {
                Long id = sceneActivelyBo.getId();
                try {
                    for (Script script : Scripts) {
                        InvokeReqDto reqDto = new InvokeReqDto();
                        String serialNumber = script.getSerialNumber();
                        Long source = script.getSource();
                        //产品执行
                        if(source==3){
                            //获取产品下面的设备信息
                            reqDto.setStrings(sceneService.selectDevice(script.getProductId()));
                        }
                        else{
                            reqDto.setSerialNumber(serialNumber);
                        }
                        reqDto.setIdentifier(script.getId());
                        reqDto.setModelName(script.getId());
                        reqDto.setProductId(script.getProductId());
                        reqDto.setType(2);
                        Map<String, Object> params = new HashMap<>();
                        params.put(script.getId(), script.getValue());
                        reqDto.setRemoteCommand(params);
                        reqDto.setParams(new JSONObject(reqDto.getRemoteCommand()));
                        reqDto.setUserId(userId);
                        functionInvoke.invokeNoReply(reqDto);
                        // 添加200毫秒延迟
                        try {
                            Thread.sleep(200);
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                            System.err.println("延迟被中断: " + e.getMessage());
                        }
                    }
                }finally {
                    Map_STR_BOOL.put(id,false);
                }
            }
        });
        return AjaxResult.success("指令下发成功");
    }

    /**
     * 实时状态
     *
     * @param serialNumber 设备编号
     * @return 结果
     */
    @GetMapping(value = "/running")
    @ApiOperation(value = "实时状态")
    public AjaxResult runState(String serialNumber) {
        List<ThingsModelValueItem> valueItemList = runtimeService.runtimeBySerialNumber(serialNumber);
        return AjaxResult.success(valueItemList);
    }

    /**
     * 根据messageId查询服务回执
     */
    @GetMapping(value = "fun/get")
    ////@PreAuthorize("@ss.hasPermi('iot:service:get')")
    @ApiOperation(value = "根据messageId查询服务回执", httpMethod = "GET", response = AjaxResult.class, notes = "根据messageId查询服务回执")
    public AjaxResult reply(String serialNumber, String messageId) {
        if (StringUtils.isEmpty(messageId) || StringUtils.isEmpty(serialNumber)) {
            throw new ServiceException(MessageUtils.message("runtime.message.id.null"));
        }
        return AjaxResult.success();
    }

    /*
    // 与新版本代码重复
    @GetMapping(value = "prop/get")
    @ApiOperation(value = "属性读取", httpMethod = "GET", response = AjaxResult.class, notes = "属性读取")
    public AjaxResult propertyGet() {
        propGetService.fetchProperty();
        return AjaxResult.success();
    }*/

    /**
     * 实时状态
     *
     * @param serialNumber 设备类型
     * @param type         物模型类型
     * @return 结果
     */
    @GetMapping(value = "/runState")
    @ApiOperation(value = "实时状态")
    public AjaxResult runState(String serialNumber, Integer type, Long productId, Integer slaveId) {
        ThingsModelType modelType = ThingsModelType.getType(type);
        List<DeviceLog> logList = runtimeService.runtimeBySerialNumber(serialNumber, modelType, productId, slaveId);
        return AjaxResult.success(logList);
    }

    /**
     * 设备服务下发日志
     */
    @GetMapping(value = "/funcLog")
    @ApiOperation(value = "设备服务下发日志")
    public TableDataInfo funcLog(String serialNumber) {
        startPage();
        List<FunctionLog> logList = runtimeService.runtimeReply(serialNumber);
        return getDataTable(logList);
    }

    @GetMapping(value = "prop/get")
    @ApiOperation(value = "主动采集", httpMethod = "GET", response = AjaxResult.class, notes = "主动采集")
    public AjaxResult propGet(VariableReadVO readVO) {
        deviceMessageService.readVariableValue(readVO);
        return AjaxResult.success();
    }


}
